//
//  CycleAccurateMemoryModel.cpp
//  sad
//
//  Created by Christopher Kemsley on 2/6/12.
//  Copyright (c) 2012 iAppolopolis. All rights reserved.
//

#include "MemoryInterface.hpp"
#include "SystemCDefinitions.hpp"

#include "Settings.hpp"
#include "Codec.hpp"
#include "ModuleFactory.hpp"
#include "PubSub.hpp"

#include <string>
#include <stdio.h>

#include "MEMORY_RTL.hpp"




struct	MemoryRTLSettings : public Settings
{
	std::string		interfaceName ;
	URL		url ;
	SCInterval		waitTimeout ;
	Subscription<sc_signal<SCLogic> >	clock ;
	
	
	MemoryRTLSettings ( std::string const & name )
	:	Settings(name)
	,	interfaceName	( "memory" )
	,	url				( "memory.txt" )
	,	waitTimeout		( 1 , kSCIntervalMiliSeconds )
	,	clock			( "globalClock" )
		{
		add ( "interfaceName" , interfaceName ) ;
		add ( "path" , url ) ;
		add ( "clockSignal" , clock ) ;
		add ( "waitTimeout" , waitTimeout ) ;
		}
} ;










class	CycleAccurateFileInitializedMemory : public ConfiguredModel<MemoryRTLSettings> , virtual public simple_mem_if
{
public:
	
	/** Structure **/
	
	MEMORY_RTL*					mMemory ;
	Publication<simple_mem_if>	mPublication ;
	
	/*	Memory interface:
	 
	 sc_in<sc_logic> Clk;
	 sc_in<sc_logic> Ren, Wen;
	 sc_in<unsigned> Addr;
	 sc_in<unsigned> DataIn;
	 sc_out<unsigned> DataOut;
	 sc_out<sc_logic> Ack;
	 */
	
	sc_signal	<sc_logic>	signal_readEnable	;
	sc_signal	<sc_logic>	signal_writeEnable	;
	sc_signal	<unsigned>	signal_address		;
	sc_signal	<unsigned>	signal_dataInput	;
	sc_signal	<unsigned>	signal_dataOutput	;
	sc_signal	<sc_logic>	signal_acknowledge	;
	
	sc_out	<sc_logic>	io_readEnable		;
	sc_out	<sc_logic>	io_writeEnable		;
	sc_out	<unsigned>	io_address			;
	sc_out	<unsigned>	io_dataInput		;
	sc_in		<unsigned>	io_dataOutput		;
	sc_in		<sc_logic>	io_acknowledge		;
	
	
	
	
	
	/** Construction **/
	
	CycleAccurateFileInitializedMemory ( ModelVariant const & name )
	:	ConfiguredModel<MemoryRTLSettings> ( name )
	,	mPublication		()
	,	signal_readEnable	()
	,	signal_writeEnable	()
	,	signal_address		()
	,	signal_dataInput	()
	,	signal_dataOutput	()
	,	signal_acknowledge	()
	,	io_readEnable		()
	,	io_writeEnable		()
	,	io_address			()
	,	io_dataInput		()
	,	io_dataOutput		()
	,	io_acknowledge		()
		{
		// Construct the underlying memory
		std::string	_name	( name.instanceName ) ;	_name += "_underlying" ;
		mMemory = new MEMORY_RTL ( _name.c_str() , mConfig.url.absolutePath().c_str() ) ;
		
		// Publish the interface
		mPublication = mConfig.interfaceName ;
		mPublication = this ; // templated already for simple_mem_if*
		
		// Connect the clock
		bindIOToSignal ( &mMemory->Clk , mConfig.clock ) ;
		
		// Connect the other signals
		bindIOToSignal ( &io_readEnable		, &signal_readEnable ) ;
		bindIOToSignal ( &io_writeEnable	, &signal_writeEnable ) ;
		bindIOToSignal ( &io_address		, &signal_address ) ;
		bindIOToSignal ( &io_dataInput		, &signal_dataInput ) ;
		bindIOToSignal ( &io_dataOutput		, &signal_dataOutput ) ;
		bindIOToSignal ( &io_acknowledge	, &signal_acknowledge ) ;
		
		bindIOToSignal ( &mMemory->Ren		, &signal_readEnable ) ;
		bindIOToSignal ( &mMemory->Wen		, &signal_writeEnable ) ;
		bindIOToSignal ( &mMemory->Addr		, &signal_address ) ;
		bindIOToSignal ( &mMemory->DataIn	, &signal_dataInput ) ;
		bindIOToSignal ( &mMemory->DataOut	, &signal_dataOutput ) ;
		bindIOToSignal ( &mMemory->Ack		, &signal_acknowledge ) ;
		}
	virtual ~CycleAccurateFileInitializedMemory ()
		{
		delete mMemory ;
		}
	
	
	
	
	
	
	/** Memory Interface **/
	/*
	 Note that this interface is will wait (up until a timeout) for the memory to acknowledge,
	 then wait for its ack signal to fall.
	 */
	
    virtual bool	Write	( unsigned int addr , unsigned int  data )
		{
		io_address.write ( addr ) ;
		io_dataInput.write ( data ) ;
		
		// Write enable, wait for ack, write disable
		io_writeEnable.write ( kSCLogicTrue ) ;
		Log::states("Memory::Write") << "Waiting for acknowledge" ;
		wait ( mConfig.waitTimeout , io_acknowledge.posedge_event() ) ;
		Log::states("Memory::Write") << "DONE Waiting for acknowledge. " << (io_acknowledge.read()==kSCLogicFalse?"Timed out":"OK") ;
		io_writeEnable.write ( kSCLogicFalse ) ;
		
		// We read it; so, wait for it to fall
		if ( io_acknowledge.read() == kSCLogicTrue )
			{
			Log::states("Memory::Write") << "Waiting for acknowledge to fall" ;
			wait ( io_acknowledge.negedge_event() ) ;
			Log::states("Memory::Write") << "DONE Waiting for acknowledge to fall" ;
			}
		
		
		return io_acknowledge.read() == kSCLogicTrue ;
		}
    virtual bool	Read	( unsigned int addr , unsigned int& data )
		{
		io_address.write ( addr ) ;
		
		io_readEnable.write ( kSCLogicTrue ) ;
		Log::states("Memory::Read") << "Waiting for acknowledge" ;
		wait ( mConfig.waitTimeout , io_acknowledge.posedge_event() ) ;
		Log::states("Memory::Read") << "DONE Waiting for acknowledge. " << (io_acknowledge.read()==kSCLogicFalse?"Timed out":"OK") ;
		io_readEnable.write ( kSCLogicFalse ) ;
		
		if ( io_acknowledge.read() == kSCLogicTrue )
			{
			data = io_dataOutput.read () ;
			Log::states("Memory::Read") << "Waiting for acknowledge to fall" ;
			wait ( io_acknowledge.negedge_event() ) ;
			Log::states("Memory::Read") << "DONE Waiting for acknowledge to fall" ;
			return true ;
			}
		
		return false ;
		}
	
} ;



TypedModuleMaker<CycleAccurateFileInitializedMemory>	_CycleAccurateFileInitializedMemory ( "Memory.file.cycleAccurate" ) ;



